#!/bin/bash
## OptimLib config script for tests

function print_help
{
    echo "" ;
    echo -e "\x1B[32mOptimLib Test Configuration Options:\033[0m" >&2 ;
    echo "  -c    Coverage build" ;
    echo "  -d    Development build" ;
    echo "  -g    Debugging build (optimization flags set to -O0 -g)" ;
    echo "  -h    Print help" ;
    echo "  -i    OptimLib install path (default: build directory)" ;
    echo "          Example: /usr/local" ;
    echo "  -l    Choice of linear algebra library" ;
    echo "        Examples: -l arma, or -l eigen" ;
    echo "  -m    Specify the BLAS and Lapack libraries to link against" ; 
    echo "          Examples: -m \"-lopenblas\" or -m \"-framework Accelerate\"" ;
    echo "  -o    Compiler optimization options; defaults to:" ;
    echo "          -O3 -march=native -ffp-contract=fast -DARMA_NO_DEBUG" ;
    echo "" ;
}

while getopts hcdgi:l:m:o:p option; do
    case "${option}" in
        h) print_help; exit 2;;
        c) OPTIM_COVERAGE_BUILD="y";;
        d) OPTIM_DEV_BUILD="y";;
        g) OPTIM_DEBUG_BUILD="y";;
        i) OPTIM_INSTALL_PATH=${OPTARG};;
        l) OPTIM_LINEAR_ALG_LIB=${OPTARG};;
        m) OPTIM_MATRIX_OPS=${OPTARG};;
        o) OPTIM_OPT=${OPTARG};;
        ?) print_help; exit 2;;
    esac
done

if [ -z ${CXX+x} ]; then 
    CXX=g++
fi

#

echo ""
echo -e "\x1B[32mOptimLib Test Configuration...\033[0m" >&2 ;
echo ""

# compiler checks

ARCH=$(uname -m)
IS_ARM=$(echo $ARCH | grep -i -c "arm")
IS_DARWIN=$($CXX -dumpmachine 2>&1 | grep -i -c "darwin")
GCC_COMPILER=$($CXX --version 2>&1 | grep -i -c -E "gcc")
CLANG_COMPILER=$($CXX --version 2>&1 | grep -i -c -E "clang")
APPLE_COMPILER=$($CXX --version 2>&1 | grep -i -c -E 'apple llvm')

# get working directory
WDIR=${PWD}

declare -a OPTIM_MATLIB_DIRS=("${WDIR}/../../include" "/usr/include" "/usr/local/include" "/opt/include" "/opt/local/include")

# look for Armadillo
if [[ "${OPTIM_LINEAR_ALG_LIB}" == "arma" ]]; then
    if [ -z ${ARMA_INCLUDE_PATH+x} ]; then 
        
        ARMA_INCLUDE_PATH=

        for i in "${OPTIM_MATLIB_DIRS[@]}"; do
            if [ -f "$i"/armadillo ]; then 
                ARMA_INCLUDE_PATH="$i"
                break
            fi
        done

        if [[ $ARMA_INCLUDE_PATH == "" ]]; then
            echo -e "  \x1B[31m- error: cannot find the Armadillo library header files.\033[0m" >&2 ;
            echo -e "  \x1B[31m         Please set the ARMA_INCLUDE_PATH environment variable.\033[0m" >&2 ;
            echo -e "  \x1B[31m         Exiting.\033[0m" >&2 ;
            echo ""
            exit 1
        fi
    fi

    OPTIM_CXX_STD="-std=c++11"
    OPTIM_MATLIB_FLAGS="-DOPTIM_ENABLE_ARMA_WRAPPERS -DARMA_NO_DEBUG"
    OPTIM_MATLIB_INCLUDE_PATH=$ARMA_INCLUDE_PATH
elif [[ "${OPTIM_LINEAR_ALG_LIB}" == "blaze" ]]; then
    # OPTIM_MATLIB_FLAGS="-DOPTIM_ENABLE_BLAZE_WRAPPERS"
    # OPTIM_MATLIB_INCLUDE_PATH=$BLAZE_INCLUDE_PATH
    # CXX_STD="-std=c++14"

    echo -e "  \x1B[31m- error: Blaze not yet supported. Exiting.\033[0m" >&2 ;
    echo ""
    exit 1
elif [[ "${OPTIM_LINEAR_ALG_LIB}" == "eigen" ]]; then
    if [ -z ${EIGEN_INCLUDE_PATH+x} ]; then 
        
        EIGEN_INCLUDE_PATH=

        for i in "${OPTIM_MATLIB_DIRS[@]}"; do
            if [ -f "$i"/Eigen ]; then 
                EIGEN_INCLUDE_PATH="$i"
                break
            fi
        done

        if [[ $EIGEN_INCLUDE_PATH == "" ]]; then
            echo -e "  \x1B[31m- error: cannot find the Eigen library header files.\033[0m" >&2 ;
            echo -e "  \x1B[31m         Please set the EIGEN_INCLUDE_PATH environment variable.\033[0m" >&2 ;
            echo -e "  \x1B[31m         Exiting.\033[0m" >&2 ;
            echo ""
            exit 1
        fi
    fi

    OPTIM_CXX_STD="-std=c++14"
    OPTIM_MATLIB_FLAGS="-DOPTIM_ENABLE_EIGEN_WRAPPERS"
    OPTIM_MATLIB_INCLUDE_PATH=$EIGEN_INCLUDE_PATH
else
    echo -e "  \x1B[31m- error: unrecognized linear algebra library.\033[0m" >&2 ;
    echo ""
    exit 1
fi

# set build and optimization flags

if [[ "${OPTIM_COVERAGE_BUILD}" == "y" ]]; then 
    if [ "$GCC_COMPILER" -eq "1" ]; then
        OPTIM_OPT_FLAGS="-g -O0 --coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
    else # clang:
        OPTIM_OPT_FLAGS="-g -O0 --coverage -fno-inline"
    fi
elif [[ "${OPTIM_DEBUG_BUILD}" == "y" ]]; then
    OPTIM_OPT_FLAGS="-g -O0"
else
    if [[ "${OPTIM_OPT}" == "" ]]; then
        OPTIM_OPT_FLAGS="-O3 -ffp-contract=fast"

        if [ "$IS_ARM" -eq "1" ]; then
            OPTIM_OPT_FLAGS="-mcpu=native ${OPTIM_OPT_FLAGS}"
        else
            # assumes x86
            OPTIM_OPT_FLAGS="-march=native ${OPTIM_OPT_FLAGS}"
        fi
    else
        OPTIM_OPT_FLAGS="${OPTIM_OPT}"
    fi

    if [[ "${OPTIM_PARALLEL}" == "y" ]]; then
        OPTIM_OPT_FLAGS="${OPTIM_OPT_FLAGS} -fopenmp"
    fi
fi

OPTIM_WARN_FLAGS="-Wall"

# shared library name and install path

OPTIM_SHLIB_NAME="optim"

#

if [[ "${OPTIM_INSTALL_PATH}" == "" ]]; then
    cd ../..
    OPTIM_INSTALL_PATH=${PWD}
    OPTIM_INSTALL_INCLUDE_PATH=${PWD}/include
    OPTIM_INSTALL_LIB_PATH=${PWD}
    cd ${WDIR}
else
    OPTIM_INSTALL_INCLUDE_PATH="${OPTIM_INSTALL_PATH}/include"
    OPTIM_INSTALL_LIB_PATH="${OPTIM_INSTALL_PATH}/lib"
fi

# BLAS and LAPACK settings

if [[ !(-z ${OPTIM_MATRIX_OPS+x}) ]]; then
    OPTIM_BLAS_LAPACK="${OPTIM_MATRIX_OPS}"
else
    if [[ $OSTYPE == darwin* ]] ; then
        OPTIM_BLAS_LAPACK="-framework Accelerate"
    else
        OPTIM_BLAS_LAPACK="-lblas -llapack"
    fi
fi

# final optimization checks

if [ "$IS_DARWIN" -eq "1" ] && [ "$GCC_COMPILER" -eq "1" ]; then
    OPTIM_OPT_FLAGS="-Wa,-q ${OPTIM_OPT_FLAGS}"
fi

#
# final print:

echo "  - OS:            ${OSTYPE}"
echo "  - Arch:          ${ARCH}"
echo "  - C++ compiler:  ${CXX}"

if [[ "${OPTIM_DEV_BUILD}" == "y" ]]; then
    echo "  - Build version: development"
elif [[ "${OPTIM_COVERAGE_BUILD}" == "y" ]]; then
    echo "  - Build version: coverage"
elif [[ "${OPTIM_DEBUG_BUILD}" == "y" ]]; then
    echo "  - Build version: debug"
else
    echo "  - Build version: release"
fi

echo ""

echo "  - OPTIM_LINEAR_ALG_LIB set to: ${OPTIM_LINEAR_ALG_LIB}"
echo "  - OPTIM_MATLIB_INCLUDE_PATH set to:"
echo "    ${OPTIM_MATLIB_INCLUDE_PATH}"
echo "  - BLAS and Lapack libraries set to:"
echo "    ${OPTIM_BLAS_LAPACK}"

echo ""

echo "  - optimization flags:"
echo "    ${OPTIM_OPT_FLAGS}"

echo ""

echo "  - OptimLib install path:"
echo "    ${OPTIM_INSTALL_PATH}"

echo ""
echo "  - Additional notes:"

if [ "$APPLE_COMPILER" -eq "1" ] && [[ "${OPTIM_PARALLEL}" == "y" ]]; then
    echo -e "    \x1B[31m- You have enabled OpenMP, but your C++ compiler does not\033[0m" >&2 ;
    echo -e "    \x1B[31m  support this feature!\033[0m" >&2 ;
fi

if [ "$IS_DARWIN" -eq "1" ] && [ "$GCC_COMPILER" -eq "1" ]; then
    echo "    - To enable AVX features, your compiler will use the Apple LLVM"
    echo "      assembler"
fi

echo ""
echo -e "\x1B[32mConfiguration completed. Creating Makefile... \c\033[0m" >&2 ;

sed -e "s|@CXX@|${CXX}|" \
    -e "s|@OPTIM_CXX_STD@|${OPTIM_CXX_STD}|" \
    -e "s|@OPTIM_MATLIB_FLAGS@|${OPTIM_MATLIB_FLAGS}|" \
    -e "s|@OPTIM_MATLIB_INCLUDE_PATH@|${OPTIM_MATLIB_INCLUDE_PATH}|" \
    -e "s|@OPTIM_BLAS_LAPACK@|${OPTIM_BLAS_LAPACK}|" \
    -e "s|@OPTIM_WARN_FLAGS@|${OPTIM_WARN_FLAGS}|" \
    -e "s|@OPTIM_OPT_FLAGS@|${OPTIM_OPT_FLAGS}|" \
    -e "s|@OPTIM_SHLIB_NAME@|${OPTIM_SHLIB_NAME}|" \
    -e "s|@OPTIM_INCLUDE_PATH@|${OPTIM_INSTALL_INCLUDE_PATH}|" \
    -e "s|@OPTIM_INSTALL_PATH@|${OPTIM_INSTALL_LIB_PATH}|" \
    Makefile.in > Makefile

echo -e "\x1B[32mdone.\033[0m" >&2 ;
echo ""
